const btns = document.querySelectorAll("header button");
const times = document.querySelector("header b");
const mineNum = document.querySelector("header i");
const table = document.querySelector("table");
const tbody = document.querySelector("tbody");
const mask = document.querySelector(".mask");
const popup = document.querySelector('.popup');

let timer,
  timeNum,
  trs,
  mine;

// 创建棋盘并生成雷
function newGame(rows, cols, num) {
  mask.style.display = 'none'; // 隐藏遮罩层
  // 动态生成表格
  for (let i = 0; i < rows; i++) {
    let tr = document.createElement('tr');
    for (let j = 0; j < cols; j++) {
      let td = document.createElement('td');
      tr.appendChild(td);
    }
    tbody.appendChild(tr);
  }
  trs = document.querySelectorAll('tr');
  let mineArr = [];
  for (let i = 0; i < num; i++) {
    let row = Math.floor(Math.random() * trs.length); // 随机一行
    let col = Math.floor(Math.random() * trs[0].children.length); // 随机一列
    // mineArr.indexOf([row, col])输出结果都是-1，不能用indexOf去判断二维数组内是否存在某一个数组，所以转换为字符串进行判断
    if (mineArr.indexOf(row + '' + col) == -1) { // 随机数去重，确保生成固定数量的雷
      mineArr.push(row + '' + col);
      trs[row].children[col].className = 'mine'; // 给雷做标记，添加样式
    } else {
      i--; // 去除重复项
    }
  }
}

// 阻止table区域内右键菜单的默认行为
table.addEventListener('contextmenu', e => e.preventDefault());

// 游戏功能逻辑
function game() {
  trs = document.querySelectorAll('tr'); // 重新获取所有tr
  mine = document.querySelectorAll('.mine'); // 重新获取所有雷
  mineNum.innerText = mine.length; // 雷数初始化
  timeNum = 0; // 时间初始化
  times.innerText = timeNum;
  time(); // 开始计时
  // 页面不可见时，停止计时
  document.onvisibilitychange = () => document.hidden ? clearInterval(timer) : time();
  for (let i = 0; i < trs.length; i++) {
    for (let j = 0; j < trs[i].children.length; j++) { // 遍历所有小方块
      let count = 0; // 用于计算 3*3 中心点附近雷的个数
      for (let k = i - 1; k <= i + 1; k++) {
        for (let l = j - 1; l <= j + 1; l++) {
          if (trs[k] == undefined || trs[k].children[l] == undefined) {
            continue; // 跳过不存在的小方块
          } else if (trs[k].children[l].className == 'mine') {
            count++;
          }
        }
      }
      trs[i].children[j].setAttribute('count', count); // 提示小方块附近雷的个数
      trs[i].children[j].setAttribute('index-x', j); // 小方块的x轴坐标
      trs[i].children[j].setAttribute('index-y', i); // 小方块的y轴坐标
      trs[i].children[j].setAttribute('mark', false); // 初始化每个小方块的状态：未被标记，方便进行右键标记
      // 右键标记功能
      trs[i].children[j].addEventListener('contextmenu', function () {
        // 没有被左键点击过的小方块
        if (this.className !== 'oncl') {
          if (this.getAttribute('mark') == 'false') { // 没有被标记过的小方块
            this.style.backgroundImage = 'url(./img/qizi.png)';
            this.setAttribute('mark', true);
            this.onclick = null; // 被标记的小方块解除绑定左键点击事件
          } else { // 被标记过的小方块
            this.style.backgroundImage = '';
            this.setAttribute('mark', false);
            this.onclick = click; // 取消标记的小方块重新绑定左键点击事件
          }
        }
        let marks = document.querySelectorAll('td[mark="true"]'); // 每次右键，重新获取所有被标记的小方块
        mineNum.innerText = mine.length - marks.length; // 刷新剩余雷数
      });
      trs[i].children[j].onclick = click; // 给每个小方块都绑定左键点击事件
    }
  }
}

// 单击事件，独立出来方便解绑、重新绑定
function click(e) {
  let t = e.target;
  let markNum = 0;
  let x = Number(t.getAttribute('index-x')); // 列数
  let y = Number(t.getAttribute('index-y')); // 行数
  for (let m = y - 1; m <= y + 1; m++) { // 计算附近有多少被标记的，实现左键点击功能
    for (let n = x - 1; n <= x + 1; n++) {
      if (trs[m] == undefined || trs[m].children[n] == undefined) {
        continue;
      } else if (trs[m].children[n].getAttribute('mark') == 'true') {
        markNum++; // 最终的markNum就表示附近的标记个数
      }
    }
  }
  if (t.getAttribute('count') == 0) {
    autoClick(t, 0); // 自动点开数字为0的方块
  } else if (t.getAttribute('count') == markNum && t.className == 'oncl') {
    // 如果这个方块上的数字与附近的标记数相等，会自动点开除标记格以外的附近3*3方块（前提：被点击过）
    autoClick(t, markNum);
  }
  if (t.className !== 'mine') { // 点开不是雷的方块
    t.className = 'oncl';
    showNum(t);
  } else { // 点到雷
    fail();
    t.style.backgroundColor = 'red';
  }
  let oncls = document.querySelectorAll('.oncl'); // 每次左键单击，重新获取被点开的所有方块
  // 游戏胜利的条件
  if (oncls.length + mine.length == trs.length * trs[0].children.length) success();
}

// 显示数字图片
function showNum(obj) {
  if (obj.className !== 'mine' && obj.getAttribute('count') !== 0) { // 数字为0时不显示图片
    obj.style.backgroundImage = `url(./img/saolei${obj.getAttribute('count')}.png)`;
  }
}

// 自动点开数字为0的方块
function autoClick(obj, num) {
  if (obj.getAttribute('count') == num) {
    let x = Number(obj.getAttribute('index-x')); // 列数
    let y = Number(obj.getAttribute('index-y')); // 行数
    // 不转换为数字类型的话，x+1/y+1 就会变成字符串拼接，无法正常比较
    for (let i = y - 1; i <= y + 1; i++) {
      for (let j = x - 1; j <= x + 1; j++) {
        if (trs[i] == undefined || trs[i].children[j] == undefined) {
          continue;
        } else if (trs[i].children[j].className !== 'oncl') { // 跳过已被点开的方块，避免死递归
          if (trs[i].children[j].getAttribute('mark') == 'false') { // 跳过被标记的方块
            if (trs[i].children[j].className == 'mine') {
              fail(); // 标记错误导致自动点到雷，也会游戏失败
              trs[i].children[j].style.backgroundColor = 'red';
            } else { // 不是雷则点开该方块
              showNum(trs[i].children[j]);
              trs[i].children[j].className = 'oncl';
              if (trs[i].children[j].getAttribute('count') == 0) {
                autoClick(trs[i].children[j], 0); // 自动点开一大片方块
              }
            }
          }
        }
      }
    }
  }
}

// 游戏胜利
function success() {
  // 游戏胜利/失败的弹框样式设置，大小与 table 相同
  mask.style.display = 'block';
  mask.style.width = table.offsetWidth + 'px';
  mask.style.height = table.offsetHeight + 'px';
  popup.innerText = '游戏胜利\n' + '您的成绩为' + timeNum + '秒';
  clearInterval(timer);
  document.onvisibilitychange = null; // 避免游戏结束后，页面可见性变化，计时又重新开始
}

// 游戏失败
function fail() {
  mask.style.display = 'block';
  mask.style.width = table.offsetWidth + 'px';
  mask.style.height = table.offsetHeight + 'px';
  popup.innerText = '游戏失败';
  clearInterval(timer);
  document.onvisibilitychange = null;
  mine = document.querySelectorAll('.mine');
  mine.forEach(item => {
    item.style.backgroundImage = 'url(./img/saolei.png)';
    item.style.boxShadow = 'none';
  });
}

// 计时函数
function time() {
  clearInterval(timer); // 先清除之前开启的定时器，防止开启多个定时器
  timer = setInterval(() => {
    timeNum++;
    times.innerText = timeNum;
  }, 1000);
}

newGame(9, 9, 10) // 未点击按钮时默认显示简单难度
game(); // 游戏开始

btns[0].onclick = () => {
  tbody.innerHTML = '';
  newGame(9, 9, 10);
  game();
}

btns[1].onclick = () => {
  tbody.innerHTML = '';
  newGame(16, 16, 40);
  game();
}

btns[2].onclick = () => {
  tbody.innerHTML = '';
  newGame(16, 30, 99);
  game();
}

btns[3].onclick = () => {
  tbody.innerHTML = '';
  let a = prompt("请输入行数");
  let b = prompt("请输入列数");
  let c = prompt("请输入雷数");
  newGame(a, b, c);
  game();
}

/*
  扫雷介绍：https://baike.baidu.com/item/%E6%89%AB%E9%9B%B7/12543
  功能思路：
    -可调整难度 => 动态生成表格和雷的个数

    -随机生成雷 => 每次循环创建两个随机数，分别作为行数和列数，tr[a].children[b]即可获得随机的方块，添加样式，成为地雷 (最好做个可识别的标记，方便下一步获得个数，例如className)

    -计算附近3*3方块内的雷的个数 => 四层for循环，第一、二层遍历整个棋盘的方块，第三、四层遍历某个方块附近的3*3方块 (从a-1到a+1，从b-1到b+1)
    -如果是计算棋盘边角的方块的3*3方块内的雷的个数，会因为tr[a-1].children[b-1]（或其它方块）不存在而报错！
    -可以使用循环中的关键字：continue 跳过不存在的小方块，并将结果储存起来（方便后面显示数字图片）

    -右键单击
      -在table区域内阻止浏览器右键菜单的默认行为
      -点击未被点击的方块，标记该方块 => 添加样式、标记（方便自动点开功能计数），解绑左键单击事件
      -点击已被标记的方块，取消标记 => 还原为未被点击时的样式，重新绑定左键单击事件

    -左键单击
      -点开未点击过的普通方块 => 添加样式、标记（方便判定胜利时计数）
        -该方块的提示数字以图片形式显示（非必需功能，可以直接用innerHTML）=> 获取方块附近雷的个数，将对应的图片作为方块的背景图片（类似图片时钟的案例）
        -如果这个方块上的数字是0，自动点开附近3*3方块，且如果周围方块里也有数字为0的方块，继续自动点开（就是一下子点开一大片方块的效果）=> 递归
      -点开已点击过的普通方块
        如果这个方块上的数字与附近的标记数相等，会自动点开除标记格以外的附近3*3方块 => if判断，符合条件就对周围方块调用单击事件函数里的代码块，如果玩家标记错误导致自动点到雷，就判定玩家输了
      -点开雷 => 触发失败的函数
      -点击标记方块，点不开 => 被标记的方块会解绑左键单击事件

    -游戏胜利 
      - 胜利条件为已点开的方块个数与雷的个数之和等于小方块的总个数 => 每次单击时进行判断
      - 弹出弹框与遮罩层（遮罩层覆盖整个棋盘，主要作用是避免棋盘再次发生点击事件）=> 提前在css中写好样式，默认隐藏，游戏胜利时改为显示

    -游戏失败 
      - 点击/自动点击到雷 => 获取所有雷的dom元素，绑定单击事件，被点击时触发失败函数
      - 翻开所有雷，玩家踩到的雷显示为红色 => 遍历所有雷，设置为点开的样式，被点击的雷的背景色设置为红色

    -游戏时长计算 => 游戏开始即开始计时器，游戏胜利或失败时清除定时器
      -页面最小化或切换页面时计时暂停（非必需功能） => 利用 visibilitychange 事件和 document.hidden 属性

    -计算还剩多少雷 => 雷的个数减去被标记的个数，绑定在右键单击事件上（不需要考虑玩家标记是否正确）
*/